package application.fileTransfer;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

import GUI.FileTransferGUI;
import application.Application;
import application.ControlConnectionToTransport;
import application.DataConnectionToTransport;

public class FileTransfer extends Thread implements Application {
	/**
	 * The controlconnection to the transportlayer
	 */
	private ControlConnectionToTransport controlConnection;
	/**
	 * The dataconnection to the transportlayer
	 */
	private DataConnectionToTransport dataConnection;
	/**
	 * Boolean that is states that the application is connected
	 */
	private boolean connected;
	/**
	 * The gui of the FileTransfer
	 */
	FileTransferGUI gui;
	/**
	 * Constructs a new FileTransfer application. It sets up a ControlConnection but doesn't send anything over the connection yet.
	 */
	public FileTransfer(FileTransferGUI gui) {
		connected = false;
		this.gui = gui;
		try {
			controlConnection = new ControlConnectionToTransport(
					Constants.CONTROL_PORT, this);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void disconnected() {
		// TODO Auto-generated method stub
		
	}
	/**
	 * Returns if the application has an open data connection
	 * @return true if the application has a connection, false if it doesn't have a connection
	 */
	public boolean isConnected(){
		return this.connected;
	}

	@Override
	public void connected() {
		if (!connected) {
			gui.out("fileTransfer.connected()");
			dataConnection = controlConnection.getDataConnection();
			connected = true;
			gui.connected();
			
		}
		
	}
	/**
	 * Method that asks the transport layer to set up a connection
	 * @param port The port to connect to
	 * @param tpa The tpa to connect to
	 */
	public void connect(int port, int tpa) {
		controlConnection.connect(port, tpa);
	}
	
	/**
	 * Makes this FileTransfer listen for incoming connections.
	 * @param port The port to listen on
	 */
	public void listen(int port) {
		controlConnection.listen(port);
	}
	/**
	 * Disconnects this FileTransfer
	 */
	public void disconnect() {
		controlConnection.disconnect();
		connected = false;
	}

	/**
	 * Sends the file with the given fileName
	 * @param fileName
	 */
	public void sendFile(String fileName) {
		File file = new File(fileName);
		FilePacket packet = new FilePacket(fileName, (int)file.length());
		gui.out("Lengte van het te versturen bestand: " + packet.getLength());
		BufferedOutputStream out = new BufferedOutputStream(dataConnection.getOutputStream());
		try {
			out.write(packet.toByteArray());
			out.flush();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		FileInputStream fis = null;
		BufferedInputStream bis = null;
		try {
			fis = new FileInputStream(file);
			bis = new BufferedInputStream(fis);
			int result = 0;
			while (result != -1) {
				byte[] data = new byte[transport.Constants.MAX_PAYLOAD_LENGTH];
				result = bis.read(data);
				if (result != -1){
					if (result != transport.Constants.MAX_PAYLOAD_LENGTH){
						byte[] temp = new byte[result];
						for (int i = 0; i < result; i++){
							temp[i] = data[i];
						}
						data = temp;
					}
					dataConnection.getOutputStream().write(data);
				}
			}
			fis.close();
			bis.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
//		disconnect();
    }
	/**
	 * Makes this FileTransfer wait for an incoming file.
	 * @throws IOException
	 */
	public void receiveFile() throws IOException{
		
		while(!isConnected()){
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		
		long time = System.currentTimeMillis();
		boolean headerReceived = false;
		ArrayList<Byte> header = new ArrayList<Byte>();
		BufferedInputStream in = new BufferedInputStream(dataConnection.getInputStream());
		while (!headerReceived){
			byte last = (byte)in.read();
			header.add(last);
			if (last == (byte)Constants.DELIMITER){
				for (int i = 0; i < Constants.FILE_LENGTH_BYTE_LENGTH; i++){
					header.add((byte)in.read());
				}
				headerReceived = true;
			}	
		}
		FilePacket packet = new FilePacket(header);
		gui.out("Lengte van het te ontvangen bestand: "+ packet.getLength());
		File file;
		
		String fileName = packet.getFileName();
		if(fileName.contains("\\")){
		fileName = fileName.substring(fileName.lastIndexOf('\\'));
		}
		
		fileName = "C:\\Sybe\\"+fileName;
		System.out.println(fileName);
		boolean nameNotInUse = true;
		file = new File(fileName);
		
		nameNotInUse = file.createNewFile();
		String[] arguments = fileName.split("\\.");
		System.out.println(arguments.length);
		for (int i = 1; !nameNotInUse; i++){
			String testName = new String();
			for (int j = 0; j < arguments.length -1; j++){
				testName += arguments[j];
			}
			testName += i;
			testName += '.';
			testName += arguments[arguments.length - 1];
			file = new File(testName);
			nameNotInUse = file.createNewFile();
		}
		int fileLength = (int) packet.getLength();
//		DataInputStream dataReader = new DataInputStream(
//				dataConnection.getInputStream());
		BufferedOutputStream fileOutput = new BufferedOutputStream(new FileOutputStream(file));
		for (int i = 0; i < fileLength; i++){
			fileOutput.write(in.read());
		}
		fileOutput.flush();
		fileOutput.close();
		gui.out("Time taken: " +  (System.currentTimeMillis() -time));
		gui.out("Klaar met ontvangen");
		gui.out("Started receiving");
		this.run();
	}
	/**
	 * The run method that listens for incoming files
	 */
	public void run(){
		try {
			receiveFile();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}